package com.ming.common.beetl.util;

import cn.hutool.core.text.CharSequenceUtil;
import com.ming.common.beetl.config.IvySQLManagerExtend;
import com.ming.common.beetl.config.MySqlStyle;
import com.ming.common.beetl.dynamic.DynamicEntityLoader;
import com.ming.common.beetl.entity.*;
import com.ming.common.ivy.IvyDict;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.beetl.sql.clazz.NameConversion;
import org.beetl.sql.core.*;
import org.beetl.sql.core.db.*;
import org.beetl.sql.core.nosql.*;
import org.beetl.sql.core.query.LambdaQuery;
import org.beetl.sql.ext.DebugInterceptor;
import org.beetl.sql.ext.DebugWithNameInterceptor;
import org.beetl.sql.ext.SimpleDebugInterceptor;
import org.beetl.sql.ext.Slf4JLogInterceptor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SqlUtil {

    public static HikariDataSource buildHikariDataSource(DataSourceEntity dataSourceEntity){
        HikariConfig ds = new HikariConfig();
        ds.setDriverClassName(dataSourceEntity.getDriverClassName());
        ds.setJdbcUrl(dataSourceEntity.getJdbcUrl());
        ds.setUsername(dataSourceEntity.getUsername());
        ds.setPassword(dataSourceEntity.getPassword());
        return new HikariDataSource(ds);
    }

    public static HikariDataSource buildHikariDataSource(DataSourceEntity dataSourceEntity, IvyDbDatasource ivyDbDatasource){
        HikariConfig ds = new HikariConfig();
        ds.setDriverClassName(dataSourceEntity.getDriverClassName());
        ds.setJdbcUrl(dataSourceEntity.getJdbcUrl());
        ds.setUsername(dataSourceEntity.getUsername());
        ds.setPassword(dataSourceEntity.getPassword());

        ds.setMaximumPoolSize(handleHikarVal(ivyDbDatasource.getMaximumPoolSize(), ds.getMaximumPoolSize(), 1));
        ds.setMinimumIdle(handleHikarVal(ivyDbDatasource.getMinimumIdle(), ds.getMinimumIdle(),1));
        ds.setIdleTimeout(handleHikarVal(ivyDbDatasource.getIdleTimeout(), ds.getIdleTimeout()));
        ds.setConnectionTimeout(handleHikarVal(ivyDbDatasource.getConnectionTimeout(), ds.getConnectionTimeout()));
        ds.setValidationTimeout(handleHikarVal(ivyDbDatasource.getValidationTimeout(), ds.getValidationTimeout()));
        ds.setMaxLifetime(handleHikarVal(ivyDbDatasource.getMaxLifetime(), ds.getMaxLifetime()));
        ds.setConnectionTestQuery(handleHikarVal(ivyDbDatasource.getConnectionTestQuery(), ds.getConnectionTestQuery()));
        ds.setConnectionInitSql(handleHikarVal(ivyDbDatasource.getConnectionInitSql(), ds.getConnectionInitSql()));
        ds.setPoolName(handleHikarVal(ivyDbDatasource.getPoolName(), ds.getPoolName()));
        ds.setAllowPoolSuspension(handleHikarVal(ivyDbDatasource.getAllowPoolSuspension(), ds.isAllowPoolSuspension()));
        ds.setReadOnly(handleHikarVal(ivyDbDatasource.getReadOnly(), ds.isReadOnly()));
        ds.setAutoCommit(handleHikarVal(ivyDbDatasource.getAutoCommit(), ds.isAutoCommit()));
        ds.setRegisterMbeans(handleHikarVal(ivyDbDatasource.getRegisterMbeans(), ds.isRegisterMbeans()));
        return new HikariDataSource(ds);
    }

    public static HikariDataSource buildHikariDataSource(DataSourceEntity dataSourceEntity, BaseEntity dsEntity){
        HikariConfig ds = new HikariConfig();
        ds.setDriverClassName(dataSourceEntity.getDriverClassName());
        ds.setJdbcUrl(dataSourceEntity.getJdbcUrl());
        ds.setUsername(dataSourceEntity.getUsername());
        ds.setPassword(dataSourceEntity.getPassword());

        ds.setMaximumPoolSize(handleHikarVal(dsEntity.getIntValue("maximumPoolSize"),ds.getMaximumPoolSize(),1));
        ds.setMinimumIdle(handleHikarVal(dsEntity.getIntValue("minimumIdle"),ds.getMinimumIdle(),1));
        ds.setIdleTimeout(handleHikarVal(dsEntity.getLongValue("idleTimeout"),ds.getIdleTimeout()));
        ds.setConnectionTimeout(handleHikarVal(dsEntity.getLongValue("connectionTimeout"),ds.getConnectionTimeout()));
        ds.setValidationTimeout(handleHikarVal(dsEntity.getLongValue("validationTimeout"),ds.getValidationTimeout()));
        ds.setMaxLifetime(handleHikarVal(dsEntity.getLongValue("maxLifetime"),ds.getMaxLifetime()));
        ds.setConnectionTestQuery(handleHikarVal(dsEntity.getStrValue("connectionTestQuery"),ds.getConnectionTestQuery()));
        ds.setConnectionInitSql(handleHikarVal(dsEntity.getStrValue("connectionInitSql"),ds.getConnectionInitSql()));
        ds.setPoolName(handleHikarVal(dsEntity.getStrValue("poolName"),ds.getPoolName()));
        ds.setAllowPoolSuspension(handleHikarVal(dsEntity.getBooleanValue("allowPoolSuspension"),ds.isAllowPoolSuspension()));
        ds.setReadOnly(handleHikarVal(dsEntity.getBooleanValue("readOnly"),ds.isReadOnly()));
        ds.setAutoCommit(handleHikarVal(dsEntity.getBooleanValue("autoCommit"),ds.isAutoCommit()));
        ds.setRegisterMbeans(handleHikarVal(dsEntity.getBooleanValue("registerMbeans"),ds.isRegisterMbeans()));
        return new HikariDataSource(ds);
    }

    private static <T> T handleHikarVal(T val, T dsVal){
        return val == null ? dsVal : val;
    }

    private static <T> T handleHikarVal(T val, T dsVal, T defaultVal){
        return val == null ? defaultVal : val;
    }

    public static SQLManager buildSQLManager(SQLManagerEntity entity){
        //得到一个ConnectionSource数据源
        ConnectionSource source = null;
        NameConversion nc = new UnderlinedNameConversion();
        String type = "single";
        String nameConversion = "UnderlinedNameConversion";
        String dbStyle = "MySqlStyle";
        String sqlInterceptors = null;
        if(entity.getIvyDbSqlManager() != null){
            IvyDbSqlManager ivyDbSqlManager = entity.getIvyDbSqlManager();
            type = ivyDbSqlManager.getType();
            nameConversion = ivyDbSqlManager.getNameConversion();
            dbStyle = ivyDbSqlManager.getDbStyle();
            sqlInterceptors = ivyDbSqlManager.getSqlInterceptors();
        }else if(entity.getBaseEntity() != null){
            BaseEntity baseEntity = entity.getBaseEntity();
            type = baseEntity.getStrValue("type");
            nameConversion = baseEntity.getStrValue("nameConversion");
            dbStyle = baseEntity.getStrValue("dbStyle");
            sqlInterceptors = baseEntity.getStrValue("sqlInterceptors");
        }
        if("single".equalsIgnoreCase(type)){
            source = ConnectionSourceHelper.getSingle(entity.getDataSource());
        }else{
            source = ConnectionSourceHelper.getMasterSlave(entity.getDataSource(), entity.getSlaveDataSources());
        }
        if("DefaultNameConversion".equalsIgnoreCase(nameConversion)){
            nc = new DefaultNameConversion();
        }else{
            nc = new UnderlinedNameConversion();
        }
        DBStyle style = null;
        switch (dbStyle){
            case "DamengStyle": style = new DamengStyle();break;
            case "DB2SqlStyle": style = new DB2SqlStyle();break;
            case "DerbyStyle": style = new DerbyStyle();break;
            case "Gbase8sStyle": style = new Gbase8sStyle();break;
            case "GreatSqlStyle": style = new GreatSqlStyle();break;
            case "GreenplumDBStyle": style = new GreenplumDBStyle();break;
            case "H2Style": style = new H2Style();break;
            case "KingbaseStyle": style = new KingbaseStyle();break;
            case "OpenGaussStyle": style = new OpenGaussStyle();break;
            case "Oracle12Style": style = new Oracle12Style();break;
            case "OracleStyle": style = new OracleStyle();break;
            case "PolarDBStyle": style = new PolarDBStyle();break;
            case "PostgresStyle": style = new PostgresStyle();break;
            case "ShenTongSqlStyle": style = new ShenTongSqlStyle();break;
            case "SQLiteStyle": style = new SQLiteStyle();break;
            case "SqlServer2012Style": style = new SqlServer2012Style();break;
            case "SqlServerStyle": style = new SqlServerStyle();break;
            case "ClickHouseStyle": style = new ClickHouseStyle();break;
            case "CassandraSqlStyle": style = new CassandraSqlStyle();break;
            case "DrillStyle": style = new DrillStyle();break;
            case "DruidStyle": style = new DruidStyle();break;
            case "HBaseStyle": style = new HBaseStyle();break;
            case "HiveStyle": style = new HiveStyle();break;
            case "IgniteStyle": style = new IgniteStyle();break;
            case "CouchBaseStyle": style = new CouchBaseStyle();break;
            case "MachbaseStyle": style = new MachbaseStyle();break;
            case "PrestoStyle": style = new PrestoStyle();break;
            case "TaosStyle": style = new TaosStyle();break;
            default: style = new MySqlStyle();break;
        }

        //SQLManagerBuilder 唯一必须的参数就是ConnectionSource
        SQLManagerBuilder builder = new SQLManagerBuilder(source);
        //命名转化，数据库表和列名下划线风格，转化成Java对应的首字母大写，比如create_time 对应ceateTime
        builder.setNc(nc);
        //拦截器，非必须，这里设置一个debug拦截器，可以详细查看执行后的sql和sql参数
        if(sqlInterceptors != null && CharSequenceUtil.isNotBlank(sqlInterceptors)){
            List<Interceptor> interceptorList = new ArrayList<>();
            String[] inters = sqlInterceptors.split(",");
            for (String inter : inters){
                switch (inter){
                    case "DebugInterceptor": interceptorList.add(new DebugInterceptor()); break;
                    case "DebugWithNameInterceptor": interceptorList.add(new DebugWithNameInterceptor()); break;
                    //case "SimpleCacheInterceptor": interceptorList.add(new SimpleCacheInterceptor()); break;
                    case "SimpleDebugInterceptor": interceptorList.add(new SimpleDebugInterceptor()); break;
                    case "Slf4JLogInterceptor": interceptorList.add(new Slf4JLogInterceptor()); break;
                    //case "TimeStatInterceptor": interceptorList.add(new TimeStatInterceptor()); break;
                }
            }
            builder.setInters(interceptorList.toArray(new Interceptor[0]));
        }
        //数据库风格，因为用的是H2,所以使用H2Style,
        builder.setDbStyle(style);
        builder.setSQLManagerExtend(new IvySQLManagerExtend());
        SQLManager sqlManager = builder.build();
        if(entity.getInitSql() != null){
            //DBInitHelper.executeSqlScript(sqlManager,entity.getInitSql());
        }
        /*try {
            GenerateTemplate.getInstance().autoGenSql(sqlManager,new String[]{"com.ming","com.ivy"});
        } catch (Exception e) {
            throw new RuntimeException(e);
        }*/
        return sqlManager;
    }

    public static final Map<String,DynamicEntityLoader> loaderMap = new HashMap<>();

    public static DynamicEntityLoader<BaseEntity> dynamicEntityLoader(SQLManager sqlManager){
        String name = sqlManager.getName();
        if(loaderMap.containsKey(name)){
            return loaderMap.get(name);
        }

        SQLManager defaultSQLManager = BeetlSqlUtil.NEW().getDefaultSQLManager();
        String basePackage = "com.ming";
        if(defaultSQLManager != null){
            IvyDict ivyDict = defaultSQLManager.lambdaQuery(IvyDict.class).andEq(IvyDict::getCode,"ivy_base_package").single();
            if(ivyDict != null && cn.hutool.core.util.StrUtil.isNotBlank(ivyDict.getCode())){
                basePackage = ivyDict.getValue();
            }
        }

        DynamicEntityLoader loader = new DynamicEntityLoader(sqlManager, basePackage, BaseEntity.class);
        loaderMap.put(name,loader);
        return loader;
    }

    public static Class<? extends BaseEntity> dynamicEntity(SQLManager sqlManager, String tableName){
        DynamicEntityLoader<BaseEntity> dynamicEntityLoader = dynamicEntityLoader(sqlManager);
        Class<? extends BaseEntity> clazz = dynamicEntityLoader.getDynamicEntity(tableName, BaseEntity.class);
        return clazz;
    }

    public static LambdaQuery<? extends BaseEntity> dynamicLambdaQuery(SQLManager sqlManager,String tableName){
        Class<? extends BaseEntity> clazz = dynamicEntity(sqlManager, tableName);
        LambdaQuery<? extends BaseEntity> query = sqlManager.lambdaQuery(clazz);
        return query;
    }

    public static LambdaQuery dynamicLambdaQuery(SQLManager sqlManager,Class<? extends BaseEntity> clazz){
        LambdaQuery query = sqlManager.lambdaQuery(clazz);
        return query;
    }
}
